/**
 * $Id: Matrix.java 12 2006-08-17 17:27:34Z masamic.japan $
 */

package net.masamic.utils.math.matrix;

import java.util.Vector;

import net.masamic.utils.math.CalcNumber;

/**
 * @author masamic
 *
 */
public class Matrix<V extends Number> {

	protected Vector<V> elements = null;
	protected int capacity = 0;
	protected int[] dim_elements;
	
	/**
	 * 
	 * @param dimensions
	 */
	public Matrix(int... dimensions) {
		dim_elements = dimensions;
		
		capacity = 1;
		for (int idx = 0; idx < dim_elements.length; idx++) {
			if (dim_elements[idx] == 0) {
				throw new ArithmeticException("Invalid Matrix Dimension Size");
			}
			capacity *= dim_elements[idx];
		}
		
		elements = new Vector<V>(capacity);
		elements.setSize(capacity);
	}
	
	/**
	 * 
	 * @param pos
	 * @return
	 * @throws Exception
	 */
	protected int calcElmIdx(int... pos) throws Exception {
		if (dim_elements.length != pos.length) {
			throw new ArithmeticException("Do not same Matrix Dimensions");
		}
		
		int offset = capacity;
		int elm_idx = 0;
		
		for(int idx = 0; idx < dim_elements.length; idx++) {
			
			if (pos[idx] < 0 || pos[idx] >= dim_elements[idx]) {
				throw new IndexOutOfBoundsException("Index is Out Of Bounds");
			}
			
			elm_idx = elm_idx * offset + pos[idx];
			offset /= dim_elements[idx];

		}
		
		return elm_idx;
	}
	
	/**
	 * 
	 * @param val
	 * @param pos
	 * @throws Exception
	 */
	public void setElementAt(V val, int... pos) throws Exception {
		elements.setElementAt(val, calcElmIdx(pos));
	}
	
	/**
	 * 
	 * @param pos
	 * @return
	 * @throws Exception
	 */
	public V getElementAt(int... pos) throws Exception {
		return (V)elements.get(calcElmIdx(pos));
	}
	
	/**
	 * 
	 * @param arg
	 * @return
	 * @throws Exception
	 */
	public Matrix<V> add(Matrix<V> arg) throws Exception {

		if (!dim_elements.equals(arg.getElementAt())) {
			throw new ArithmeticException("Do not match element size.");
		}
		
		for (int idx = 0; idx < capacity; idx++) {
			elements.set(idx, (V)CalcNumber.add(elements.get(idx), arg.elements.get(idx)));
		}
		
		return this;
	}

	/**
	 * 
	 * @param arg
	 * @return
	 * @throws Exception
	 */
	public Matrix<V> sub(Matrix<V> arg) throws Exception {

		if (!dim_elements.equals(arg.getElementAt())) {
			throw new ArithmeticException("Do not match element size.");
		}
		
		for (int idx = 0; idx < capacity; idx++) {
			elements.set(idx, (V)CalcNumber.sub(elements.get(idx), arg.elements.get(idx)));
		}
		
		return this;
	}
	
	/**
	 * 
	 */
	public String toString() {
		return elements.toString();
	}
	
	
	/**
	 * 
	 * @param <T>
	 * @param elements
	 * @return
	 * @throws Exception
	 */
	public static <T extends Number> Matrix<T> createMatrix(int... elements) throws Exception {
		Matrix ret = null;
		if (elements.length == 0) {
			throw new ArithmeticException("No dimension.");
		} else if (elements.length == 1) {
			ret = new net.masamic.utils.math.matrix.Vector<T>(elements[0]);
		} else if (elements.length == 2) {
			if (elements[0] == elements[1]) {
				ret = new SquareMatrix<T>(elements[0]);
			} else {
				ret = new Matrix2D<T>(elements[0], elements[1]);
			}
		} else {
			ret = new Matrix<T>(elements[0]);
		}
		return ret;
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {

		Matrix<Double> test = Matrix.createMatrix(2,2);
		test.setElementAt(10.0, 0,0);
		test.setElementAt(9.0, 0,1);
		test.setElementAt(7.0, 1,0);
		test.setElementAt(6.0, 1,1);
		
		System.out.println(test.getClass());
		System.out.println(test.toString());
		
	}

}
